package com.h117121.fashionshop.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;
import org.openid4java.OpenIDException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.AxMessage;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.ax.FetchResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;

import com.h117121.fashionshop.controller.generic.GenericController;
import com.h117121.fashionshop.core.dom.Account;
import com.h117121.fashionshop.core.dom.Account.UserType;
import com.h117121.fashionshop.core.exception.AccountNotFoundException;
import com.h117121.fashionshop.core.exception.BannedAccountException;
import com.h117121.fashionshop.core.exception.UsernameExistException;
import com.h117121.fashionshop.core.exception.WrongPasswordException;
import com.h117121.fashionshop.validator.LoginValidator;

@Controller
@RequestMapping("/login")
public class LoginController extends GenericController {
	final static String YAHOO_ENDPOINT = "https://me.yahoo.com";
	final static String GOOGLE_ENDPOINT = "https://www.google.com/accounts/o8/id";

	@Autowired
	private ConsumerManager manager;

	@Autowired
	private LoginValidator validator;

	@RequestMapping(method = RequestMethod.POST)
	public String processFormSubmit(HttpServletRequest request,
			@ModelAttribute("acc") Account account, BindingResult errors,
			SessionStatus status) {
		validator.validate(account, errors);
		if (errors.hasErrors()) {
			return "LoginPage";
		}

		try {
			Account acc = businessService.authenticate(account.getUserName(),
					account.getUserPassword());
			request.getSession().setAttribute("account", acc);

			if (acc.getUserType() == UserType.ADMIN) {
				return "redirect:/admin/home";
			} else {
				return "redirect:/home";
			}
		} catch (BannedAccountException e) {
			errors.rejectValue("userName", "banned.userName",
					"Tài khoản đã bị khóa");
			return "LoginPage";
		} catch (AccountNotFoundException e) {
			errors.rejectValue("userName", "notfound.userName",
					"Tên tài khoản không tồn tại");
			return "LoginPage";
		} catch (WrongPasswordException e) {
			errors.rejectValue("userPassword", "wrong.userPassword",
					"Mật khẩu không đúng");
			return "LoginPage";
		}
	}

	@RequestMapping(method = RequestMethod.GET)
	public String showForm(HttpServletRequest req, ModelMap model) {
		if (req.getSession().getAttribute("account") != null) {
			return "redirect:/home";
		}
		model.addAttribute("acc", new Account());
		return "LoginPage";
	}

	@RequestMapping(value = "/facebook", method = RequestMethod.GET)
	public String loginFacebook(HttpServletRequest req, ModelMap model) {

		String code = req.getParameter("code");
		if (code == null || code.equals("")) {
			// an error occurred, handle this
		}

		String token = null;
		try {
			String g = "https://graph.facebook.com/oauth/access_token?client_id=459898544025226&redirect_uri="
					+ URLEncoder
							.encode(req.getRequestURL().toString(), "UTF-8")
					+ "&client_secret=2a4aadf2e3480d9f003276f8ac1be417&code="
					+ code;
			URL u = new URL(g);
			URLConnection c = u.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(
					c.getInputStream()));
			String inputLine;
			StringBuffer b = new StringBuffer();
			while ((inputLine = in.readLine()) != null)
				b.append(inputLine + "\n");
			in.close();
			token = b.toString();
			if (token.startsWith("{"))
				throw new Exception("error on requesting token: " + token
						+ " with code: " + code);
		} catch (Exception e) {
			// an error occurred, handle this
		}

		String graph = null;
		try {
			String g = "https://graph.facebook.com/me?" + token;
			URL u = new URL(g);
			URLConnection c = u.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(
					c.getInputStream()));
			String inputLine;
			StringBuffer b = new StringBuffer();
			while ((inputLine = in.readLine()) != null)
				b.append(inputLine + "\n");
			in.close();
			graph = b.toString();
		} catch (Exception e) {
			// an error occurred, handle this
		}

		String facebookId = "";
		try {
			JSONObject json = new JSONObject(graph);
			facebookId = json.getString("id");
		} catch (JSONException e) {
			// an error occurred, handle this
		}

		Account fbAccount = new Account();
		fbAccount.setUserName("fb" + facebookId);
		fbAccount.setUserPassword(UUID.randomUUID().toString());
		try {
			businessService.register(fbAccount);
			req.getSession().setAttribute("account", fbAccount);
		} catch (UsernameExistException e1) {
			try {
				Account account = businessService.findByUsername("fb"
						+ facebookId);
				req.getSession().setAttribute(
						"account",
						businessService.authenticate(account.getUserName(),
								account.getUserPassword()));
			} catch (AccountNotFoundException e) {
			} catch (WrongPasswordException e) {
			} catch (BannedAccountException e) {
			}
		}

		return "redirect:/home";
	}

	@RequestMapping(value = "/openid", method = RequestMethod.POST)
	public void loginOpenId(
			HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam(value = "identifier", required = true) String identifier) {
		try {
			this.authRequest(identifier, request, response);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@RequestMapping(value = "/openid", method = RequestMethod.GET)
	public String returnFromLoginOpenId(HttpServletRequest req) {
		Identifier identifier = this.verifyResponse(req);
		if (identifier != null) {
			Account fbAccount = new Account();
			fbAccount.setUserName("openid" + identifier.getIdentifier());
			fbAccount.setUserPassword(UUID.randomUUID().toString());
			try {
				businessService.register(fbAccount);
				req.getSession().setAttribute("account", fbAccount);
			} catch (UsernameExistException e1) {
				try {
					Account account = businessService.findByUsername("openid"
							+ identifier.getIdentifier());
					req.getSession().setAttribute(
							"account",
							businessService.authenticate(account.getUserName(),
									account.getUserPassword()));
				} catch (AccountNotFoundException e) {
				} catch (WrongPasswordException e) {
				} catch (BannedAccountException e) {
				}
			}

			return "redirect:/home";
		} else {
			System.out.println("login with openid failed");
			return "redirect:/login";
		}
	}

	public String authRequest(String userSuppliedString,
			HttpServletRequest httpReq, HttpServletResponse httpResp)
			throws IOException {
		try {
			// configure the return_to URL where your application will receive
			// the authentication responses from the OpenID provider
			String returnToUrl = httpReq.getRequestURL().toString();

			// --- Forward proxy setup (only if needed) ---
			// ProxyProperties proxyProps = new ProxyProperties();
			// proxyProps.setProxyName("proxy.example.com");
			// proxyProps.setProxyPort(8080);
			// HttpClientFactory.setProxyProperties(proxyProps);

			// perform discovery on the user-supplied identifier
			List discoveries = manager.discover(userSuppliedString);

			// attempt to associate with the OpenID provider
			// and retrieve one service endpoint for authentication
			DiscoveryInformation discovered = manager.associate(discoveries);

			// store the discovery information in the user's session
			httpReq.getSession().setAttribute("openid-disc", discovered);

			// obtain a AuthRequest message to be sent to the OpenID provider
			AuthRequest authReq = manager.authenticate(discovered, returnToUrl);

			FetchRequest fetch = FetchRequest.createFetchRequest();
			if (userSuppliedString.startsWith(GOOGLE_ENDPOINT)) {
				fetch.addAttribute("email",
						"http://axschema.org/contact/email", true);
				fetch.addAttribute("firstName",
						"http://axschema.org/namePerson/first", true);
				fetch.addAttribute("lastName",
						"http://axschema.org/namePerson/last", true);
			} else if (userSuppliedString.startsWith(YAHOO_ENDPOINT)) {
				fetch.addAttribute("email",
						"http://axschema.org/contact/email", true);
				fetch.addAttribute("fullname",
						"http://axschema.org/namePerson", true);
			} else { // works for myOpenID
				fetch.addAttribute("fullname",
						"http://schema.openid.net/namePerson", true);
				fetch.addAttribute("email",
						"http://schema.openid.net/contact/email", true);
			}

			// attach the extension to the authentication request
			authReq.addExtension(fetch);

			httpResp.sendRedirect(authReq.getDestinationUrl(true));

		} catch (OpenIDException e) {
			// present error to the user
		}

		return null;
	}

	// --- processing the authentication response ---
	public Identifier verifyResponse(HttpServletRequest httpReq) {
		try {
			// extract the parameters from the authentication response
			// (which comes in as a HTTP request from the OpenID provider)
			ParameterList response = new ParameterList(
					httpReq.getParameterMap());

			// retrieve the previously stored discovery information
			DiscoveryInformation discovered = (DiscoveryInformation) httpReq
					.getSession().getAttribute("openid-disc");

			// extract the receiving URL from the HTTP request
			StringBuffer receivingURL = httpReq.getRequestURL();
			String queryString = httpReq.getQueryString();
			if (queryString != null && queryString.length() > 0)
				receivingURL.append("?").append(httpReq.getQueryString());

			// verify the response; ConsumerManager needs to be the same
			// (static) instance used to place the authentication request
			VerificationResult verification = manager.verify(
					receivingURL.toString(), response, discovered);

			// examine the verification result and extract the verified
			// identifier
			Identifier verified = verification.getVerifiedId();
			if (verified != null) {
				AuthSuccess authSuccess = (AuthSuccess) verification
						.getAuthResponse();

				if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
					FetchResponse fetchResp = (FetchResponse) authSuccess
							.getExtension(AxMessage.OPENID_NS_AX);

					List emails = fetchResp.getAttributeValues("email");
					String email = (String) emails.get(0);
				}

				return verified; // success
			}
		} catch (OpenIDException e) {
			// present error to the user
		}

		return null;
	}

}
